class effect
{
protected:
	IAnimatedMeshSceneNode	Nde;		//Effect Node
	stringw					Ste;		//State of Effect
	stringw					Nam;		//Name of Effect
	int						Lfe;		//Current LifeTime of Effect / Milisecond
	int						Ani[][]	=	//Animation: First Frame, Last Frame
		// Appr| Stat| Actv| Dspr
		{{    1,    1,    1,    1},
		{     1,    1,    1,    1}};
	int						VisRng;		//Visual Range Minimum
	int						ActRng;		//Active Range Minimum
	int						IntRng;		//Interactive Range Minimum

	virtual effect( IAnimatedMeshSceneNode, stringw, stringw, int, int, int, int, int );
	Update()
	{
		int	FPS	= IVideoDriver::getFPS();	//Current Frames Per Second
		switch( this->Ste )
		{
			case "Appear":
				if( Ani )
					if( this->Nde.getStartFrame() !! this->Ani[0][0] )
						this->Nde.setFrameLoop( this->Ani[0][0], this->Ani[0][1] );
				if( Lfe ) Lfe -= ( 1000 / FPS );
				else this->Ste = "Disappear";
				break;
				
			case "Static":
				if( Ani )
					if( this->Nde.getStartFrame() !! this->Ani[1][0] )
						this->Nde.setFrameLoop( this->Ani[1][0], this->Ani[1][1] );
				if( Lfe ) Lfe -= ( 1000 / FPS );
				else this->Ste = "Disappear";
				break;
				
			case "Active":
				if( Ani )
					if( this->Nde.getStartFrame() !! this->Ani[2][0] )
						this->Nde.setFrameLoop( this->Ani[2][0], this->Ani[2][1] );
				if( Lfe ) Lfe -= ( 1000 / FPS );
				else this->Ste = "Disappear";
				break;
				
			case "Disappear":
				if( Ani )
					if( this->Nde.getStartFrame() !! this->Ani[3][0] )
						this->Nde.setFrameLoop( this->Ani[3][0], this->Ani[3][1] );
				if( Lfe <= NULL ) this	delete;
				else Lfe -= ( 1000 / FPS );
				break;
		};
	};

	virtual ~effect();
private:

	list	Cnt;	//List of Effects
};
effect::effect( ISceneNode ENde, stringw ESte, stringw ENam, int ELfe, int Ani[][], int EVis, int EAct, int EInt )
{
		this->Nde	= ENde;
		this->Ste	= ESte;
		this->Nam	= ENam;
		this->Lfe	= ELfe;
		this->Vis	= EVis;
		this->Act	= EAct;
		this->Int	= EInt;

		effect::Cnt.push_back( this );

		return this;
};
effect::~effect()
{
		this->Nde.Remove();
		effect::Cnt.erase( this );
};

class projectile : public effect
{
public:
	int				Tpe;		//Projectile Type
	IAnimatedMesh	Msh;		//Projectile Mesh
	vector3df		Vlc;		//Projectile Velocity
	vector3df		Dsp;		//Displacement from Last Position
	vector3df		LstLstPos;	//Last Last Position
	vector3df		LstPos;		//Last Position

	projectile( int PTpe, vector3df PPos, vector3df PRot, vector3df PVlc, stringw PSte, int PAni )
	{
		this->Tpe	= PTpe;
		this->Nde.setPosition( PPos );
		this->Nde.setRotation( PRot );
		this->Vlc	= PVlc;
		this->Ste	= PSte;
		this->Ani	= PAni;

		effect::Cnt.push_back( this );
		projectile::Cnt.push_back( this );

		return this;
	}
	Update()
	{
		for( projectile P in projectile::Cnt())
		{
			//Update Position
			P.LstPos	= P.Nde.getAbsolutePosition();			//Set Last Position
			P.Nde.SetPosition( P.Nde.getPosition() + P.Vlc );	//Set Current Position
			//Update Rotation

			//Update Collision
			if(	P.LstPos )
			{
				//Test Bounding Box Collision
				lie3df		Pth		= line3df<s32>( P.LstPos, P.Nde.getAbsolutePosition());
				float		ColRad	= Pth.GetStart().getDistanceFrom( Pth.GetEnd());
				vector3df	PrjPos	= P.Nde.getAbsolutePosition();
				//ISceneNode	Colide	= smgr->getSceneCollisionManager().getSceneNodeFromRayBB( Path ) //Projectile Collision

				//Projectile Expire
				//if( P.Nde.getAbsolutePosition().getDistanceFrom( Obj.Nde.getAbsolutePosition()) > float( projectile.PrjInf[4][P.Style] ))
				//	P.Destroy();
				//
				//else if( ColRad )
				if(	P.Lfe <= 0 )
				{
					P.Destroy();
				//else if( Colide )
				//{
				//	for each( debris D in debris.Cnt )
				//	{
				//		if(	ColRad < D.Node.getAbsolutePosition().getDistanceFrom( PPos ))
				//			if(	Colide.Compare( D.Node ))
				//				DebrisMesh:IAnimatedMesh = Scene.getMesh( "media/Asteroid1.ms3d" ); //Debris Mesh
				//				selector:ITriangleselector = Scene.createTriangleselector( DebrisMesh.getMesh( 0 ), D.Node ); //Triangle selector
				//				Intersection:vector3df = vector3df<s32>.Create(); //Collision Vector
				//				Tri:triangle3df = triangle3df.Create(); //Collision Triangle

				//				D.Node.setTriangleselector( selector ) //Add Triangle selector as Child

				//				//Test Mesh Collision
				//				if(	Scene.getSceneCollisionManager().getCollisionPoint( Path, selector, Intersection, Tri ));
				//					P.Colide( D, Colide.getAbsolutePosition()); //Problem with Collide Method?
				//					P.Colide( Intersection );
				//				D.Destroy();

				//				break;
				//			}
				//		}
				//	}
				}
				else
				{
					//Update Attributes
					P.Lfe -= int( 1000 / LastFPS );	//Update LifeTime
					P.Ste = NULL;					//Update State
				}
			}
			/*
			line3df Path;
			Path.setLineFromVects( P.LastPos, P.Node.getAbsolutePosition()) //Projectile Path
			ISceneNode Colide = scene.getSceneCollisionManager().getSceneNodeFromRayBB( Path ) //Projectile Collision
			
			if( Colide.getType() == ESNT_ANIMATED_MESH )
			{
				for each( particle Debris in Debris.Count())
				{
					if(	Colide.Compare( D.Node )) D.Remove();
				}
			}

			line.setLineFromVects(CAMERA.getPosition(), ..
				line.getStart().Plus((CAMERA.getTarget().Minus(line.getStart())).normalize().Mult(1000.0)))
			
			if( ( Scene.getSceneCollisionManager().getCollisionPoint( ..
				line, selector, intersection, tri ))
			
				bill.setPosition(intersection)
			
				driver.SetTransform(ETS_WORLD,matrix4.create())
				driver.setMaterial(material)
				driver.draw3DTriangle(tri,_SCOLOR(0,255,0,0))
			Endif
			
			Local Path:line3df = line3df( P.LastPos, P.Node.getAbsolutePosition() )
			
			for Local D:Debris = EachIn Debris.Count
				Local Mesh:IAnimatedMesh = Scene.getMesh( "media/Asteroid1.ms3d" )
			
				if(	D.Node.getBoundingBox().intersectsWithLine( Path ))
					BoundingBox Collision
					Local selector:ITriangleselector = Scene.createTriangleselector( Mesh.getMesh( 0 ), D.Node ) //Triangle selector
					Local Intersection:vector3df = vector3df<s32>.Create() //Collision Vector
					Local Tri:triangle3df = triangle3df.Create() //Collsion Triangle
			
					D.Node.setTriangleselector( selector )
			
					if(	Scene.getSceneCollisionManager().getCollisionPoint( Path, selector, Intersection, Tri ))
						Mesh Collision
						P.Colide( Intersection )
						D.Destroy(  )
					}
				}
			}
			*/
		}
	};

	Destroy()
	{
		//Disappear
		this->Nde.setFrameLoop( Ani[3][0], Ani[3][1]);	//Animation
		this->Ste	= "Disappear";						//State

		//Effect
		//Sound
	};

	~projectile()
	{
		this->Nde.remove();
		projectile::Cnt.erase( this );
	};

private:
		//     Name| Speed|   Rate|   Fall|   Trail|  Sight|  Shake|   Steady| Hazard| Damage|   Life|
		//         |      |    Ms.|       |    Bool| Radius|    Use|     /Ms.|    Hp.| Radius|    Ms.|
    const stringw	PrjInf[4][11]	=	//Projectile Info Table [Nam][Attribute]
		{{"Trw Str",   "7",  "500",  "0.2", "False",   "16", "1.50", "0.0125",    "5",    "0", "2000"},
		{ "Trw Knf",   "5", "1000",  "0.5", "False",   "24",    "2", "0.0125",    "7",    "0", "1700"},
		{ "Grp Hok",  "-1",    "0",  "0.5",  "True",    "0",    "0",      "0",  "0.5",    "0",     ""},
		{ "Zip Lin",  "-1",    "0",  "0.5",  "True",    "0",    "0",      "0",  "0.5",    "0",     ""}}
	list			Cnt;				//List of Projectiles

	Use( int PrjTpe, user Usr )
	{
		projectile		P;		//New Projectile
		IAnimatedMesh	M	=	//Projectile Mesh
			smgr.getMesh( "media/" + projectile.PrjInf[PrjTpe][0] + ".ms3d" );

		//Check Projectile Mesh
		if(	M && M.IsValid() == true )
		{
			//Create Projectile
			P.Msh	= M;	//Projectile Mesh
			P.Nde	=		//Projectile Node
				smgr.addAnimatedMeshSceneNode( P.Msh, NULL, NULL, user::Nde.getAbsolutePosition(), user::Nde.GetRotation());
		}

		//Projectile Mesh Invalid
		else
		{
			//Display Error Message
			cout << "Projectile Mesh Invalid" << endl;
			break;
		}

		//Check Projectile Node
		if(	P.Nde.IsValid() == true )
		{
			cout << "Prj Nde Succeded" << endl;
			//Tracer Throw
			if(	projectile.PrjInf[PrjTpe][4] == "False" )
			{
				cout << "Prj Tracer Succeded" << endl;

				P.Nde.setMaterialType( EMT_TRANSPARENT_ADD_COLOR );	//Material Type
				P.Nde.setMaterialFlag( EMF_LIGHTING, false );			//Material Lighting

				//Set Velocity
				matrix4		M1;
				//matrix4	M2;
				matrix4		M3;
				vector3df	PrjFwd	= vector3df<s32>( 0, 0, float( projectile.PrjInf[PrjTpe][1] ));
				//vector3df	Acc		= vector3df<s32>( 0, RandRange( -ActAcc[Amm[2]] / 2 * 100, ActAcc[Eqp[0]] / 2 * 100 ) / 100, RandRange( 0, 359 )); //Accuracy

				P.LstPos	= ( P.Nde.getAbsolutePosition() + ( vector3df<s32>( 0, 40, 0 )));	//Set Last Position
				M1.setRotationDegrees( Usr.Nde.GetRotation());
				//M2.setRotationDegrees( Acc());
				//M1.MultEq( M2 );
				P.Nde.SetRotation( M1.getRotationDegrees());	//Set Rotation
				M3.makeIdentity();
				M3.setRotationDegrees( P.Nde.GetRotation());
				M3.transformVect( PrjFwd );
				P.Vlc	= PrjFwd += ( Usr.Vlc );	//Set Velocity
			}

			//Rope Throw
			else if( projectile.PrjInf[PrjTpe][4] == "True" )
			{
				cout << "Prj Rope Succeded" << endl;

				P.Nde.setMaterialType( EMT_SOLID );				//Material Type
				P.Nde.setMaterialFlag( EMF_LIGHTING, true );	//Material Lighting

				//Set Velocity
				matrix4	M1;
				//matrix4	M2;
				matrix4	M3;
				vector3df	PrjFwd	= vector3df<s32>( 0, 0, float( projectile::PrjInf[PrjTpe][1] ));
				//vector3df	Acc	= vector3df<s32>( 0, RandRange( -ActAcc[Amm[2]] / 2 * 100, ActAcc[Eqp[0]] / 2 * 100 ) / 100, RandRange( 0, 359 )); //Accuracy

				P.LstPos	= ( P.Nde.getAbsolutePosition() + ( vector3df<s32>( 0, 40, 0 )));	//Set Last Position
				M1.setRotationDegrees( Usr.Nde.GetRotation());
				//M2.setRotationDegrees( Acc );
				//M1.MultEq( M2 );
				P.Nde.SetRotation( M1.getRotationDegrees());	//Set Rotation
				M3.makeIdentity();
				M3.setRotationDegrees( P.Nde.GetRotation());
				M3.transformVect( PrjFwd );
				P.Vlc	= PrjFwd;	//Set Velocity
			}

			//P.Nde.setMaterialTexture( 0, driver.getTexture( "media/" + projectile.PrjInf[PrjTpe][0] + " Mat.tga" )) //Projectile Texture
			if(	Amm[3] == 0 )			//Use Rate
			{
				//Set Weapon Use Rate
				ActTmr[0] = int( WpnInf[Eqp[0]][2] );
			}
			else if( Amm[3] == 1 )
			{
				//Set Item Use Rate
				ActTmr[1] = int( EqpInf[Eqp[1]][2] );
			}
			P.Tpe	= PrjTpe;						//Set Projectile Type
			P.Lfe	= int( PrjInf[PrjTpe][10] );	//Set Initial LifeTime
			P.Ste	= NULL;							//Set Initial State
			if(	Amm[3] == NULL ) --Amm[1];			//Subtract Round
			if(	Amm[3] == 1 ) --Amm[2];				//Subtract Ammount
			AphInd[1]	= 100;						//Apply Rounds Indicator

			//Insert Projectile
			effect::Cnt.push_back( P );
			projectile::Cnt.push_back( P );

			//Subtract Accuracy
			//if( ActAcc[Amm[2]] < float( projectile.PrjInf[Eqp[0]][5] )
				//ActAcc[Amm[2]] += float( projectile.PrjInf[Eqp[0]][7] );
			//if( ActAcc[Amm[2]] < float( projectile.PrjInf[Eqp[1]][5] )
				//ActAcc[Amm[2]] += float( projectile.PrjInf[Eqp[1]][7] );
		}

		//Projectile Node Invalid
		else
		{
			//Display Error Message
			cout << "Projectile Node Invalid" << endl;
			break;
		}
		cout << P.Vlc.getX() << ", " << P.Vlc.getY() << ", " << P.Vlc.getZ() << endl;
		return P;
	}
};

class particle : public effect
{
public:
	int         Lfe;        //Particle Elapsed Ms.
	int			Tmr;		//Particle Expiration Ms.

	particle( int PLfe, int PTmr )
	{
		this->Lfe	= PLfe;
		this->Tmr	= PTmr;

		effect::Cnt.push_back( this );
		particle::Cnt.push_back( this );

		return this;
	};

	Update()
	{
	};

	~particle()
	{
		effect::Cnt.erase( this );
		particle::Cnt.erase( this );

		return this;
	};
private:
	list	Cnt;	//List of Particles
};
